classdef ET_SS_FF < ET_SS_CYCLIC
    % This class enforces that all trials should be completed by recording
    % which trials were aborted.
    
    properties
       distractors
       % Interval until the displacement of the stimuli.
       dist_c = [255 255 255]
    end
    
    methods
        function prepareTrials(self,nt,js)
            % Prepares a matrix of the trials using information about the
            % number of the trials.
            %
            %   NT      Total number of trials displayed per block.
            %   JS      Jump size. It specifies the size of the target jump
            %           or displacement.
            
            % Counter balance left and right hemifield and left and right
            % movement of target.
            
            self.num_trials = nt;
            self.trials = repmat(js',nt / numel(js),self.num_blocks);
            
            self.distractors = self.trials;
            
            
            for i = 1 : self.num_blocks
                self.trials(:,i) = self.trials(randperm(nt),i);
            end
            
            cues = zeros(1,self.num_blocks*nt/numel(js));
            cues(1:numel(cues)/2 ) = 1;
            % Works only for two conditions
            for i = 1:numel(js)
                t = find(self.trials == js(i));
                cues = cues(randperm(numel(cues)));
                self.distractors(t(cues == 0)) = js(1);
                self.distractors(t(cues == 1)) = js(2);
            end
            
        end
        
        function [ok,goout] = expLoop_(self,trial)
            % Runs the main experimental loop.
            %
            % TRIAL     Trial number.
            
            [t,b_] = ind2sub([self.num_trials,self.num_blocks],trial);
            
            b = self.blocks(b_);
            
            % Initilize Eyelink
            if self.EyeLink
                % open a file on the eyetracker
                el = EyelinkInitDefaults(self.scn.p);
                % First two characters identify subject, %second two block
                % and last four trial.
                Eyelink('Openfile', ...
                    sprintf('%02d%02d%04d',self.exp_par.sbj,b_,t));
                Eyelink('StartRecording');
                self.sendParameters(trial);
            end
            
            
            % Creates the four point coordinates for rendering the image.
            % Cx is the center of the objet in the x axis, w it's width and
            % h its height.
            
            sp_ = @(cx,w,h) [ cx - w/2,self.scn.h/2 - h/2,cx ...
                + w/2,self.scn.h/2 + h/2];
            
            % degrees to pixels
            d2p = @(d) round(self.scn.dist*tand(d)*(self.scn.w/self.scn.wcm));
            
            
            fix_init = 0; % Time since fixation started
            phase = 0; % phase of experiment.
            target_moved = 0; % Target has been moveds.
            ok = 1; % Keep going or abort
            avail = 0; % Just in case.
            goout = 0; % Just in case.
            fixation_offset = 0;
            errortype = 'A';
            % Time accumulator
            t_a = 0;
            flip_t = 0;
            
            
            % Clean stack and have a short buffer
            KbEventFlush();
            WaitSecs(0.2);
            
            % Display fixation
            % =====================================================
            Screen('FillRect',self.scn.p,self.bc(b).bg);
            
            % Fixation
            
            cx = self.scn.w/2 - d2p(self.sacc_s/2);
            smp = sp_(cx,d2p(self.fix.w),d2p(self.fix.h));
            Screen(self.fix.shape,self.scn.p,tc(),round(smp));
            
            % Sacadde goal
            
            cx = self.scn.w/2 + d2p(self.sacc_s/2);
            smp = sp_(cx,d2p(self.fix.w),d2p(self.fix.h));
            Screen(self.fix.shape,self.scn.p,tc(),round(smp));
            
            % Target
            
            cx = self.scn.w/2;
            smp = sp_(cx,d2p(self.fix.w),d2p(self.fix.h));
            Screen(self.fix.shape,self.scn.p,tc(),round(smp));
            
            % Distractor
            
            cx = self.scn.w/2 - d2p(self.sacc_s - self.distractors(trial));
            smp = sp_(cx,d2p(self.fix.w),d2p(self.fix.h));
            Screen(self.fix.shape,self.scn.p,self.dist_c,round(smp));
            
            
            Screen( 'Flip',self.scn.p);
            fx = [self.scn.w/2- d2p(self.sacc_s/2),self.scn.h/2];
            
            t0 = GetSecs();
            
            if isunix()
                Priority(1);
            elseif ispc()
                priorityLevel=MaxPriority(self.scn.p);
                Priority(priorityLevel);
            end
            
            t_a = self.fix_buffer_t;
            w = [-d2p(2),d2p(2),d2p(2),-d2p(2)];
            % Check the keyboard only every 20 times.
            kp = 1;
            while ok
                % If the subject presses any key during the trial, it is
                % aborted This only affects the two fist phases of the
                % experiment. For performance this is done only every ten
                % cycles
                if kp == 20
                    avail = KbCheck();
                    if avail
                        if self.EyeLink
                            Eyelink('MESSAGE','ABORT TRIAL');
                        end
                        ok = 0;
                        break
                    end
                    kp = 1;
                else
                    kp = kp + 1;
                end
                
                if ~self.EyeLink
                    [mx,my] = GetMouse();
                elseif self.EyeLink
                    % get the sample in the form of an event structure
                    evt = Eyelink( 'NewestFloatSample');
                    % Get x and y from the eye selected
                    x = evt.gx(self.el_pointer.er+1);
                    y = evt.gy(self.el_pointer.er+1);
                    % do we have valid data and is the pupil visible?
                    if x~=el.MISSING_DATA && y~=el.MISSING_DATA
                        mx=x;
                        my=y;
                    else
                        WaitSecs(0.003);
                        continue
                    end
                else
                    continue
                end
                
                % Wait until the eye has fixated for more than 0.2 seconds
                % do accep fixation. If accepted wait other 0.3 secs and
                % start trial.
                
                if phase == 0
                    inw = inwindow(fx,mx,my,w);
                    if inw && fix_init >= t_a
                        % Fixation accepted, now enter the next phases
                        t_a = t_a + self.fix_t;
                        phase = 1;
                    elseif inw && 0 < fix_init && ...
                            fix_init < t_a
                        % Subject reached the goal. Now accumulating time.
                        fix_init = GetSecs() - t0;
                    elseif ~inw && 0 < fix_init && ...
                            fix_init < t_a
                        % Subject fixated but moved his eyes before the
                        % fixation was accepted. Buffer time is restarted.
                        fix_init = 0;
                    elseif inw && fix_init == 0
                        % Subject reached the fixation. Now waiting to
                        % accept the fixation.
                        t0 = GetSecs();
                        % Adds an small quatity of time
                        fix_init = 0.00001;
                    end
                    continue
                end
                
                if phase == 1
                    inw = inwindow(fx,mx,my,w);
                    if inw && fix_init >= t_a
                        % Fixation was accepted. Now continue to next
                        % phase.
                        if self.EyeLink
                            % Phase two started
                            Eyelink('MESSAGE','PHASE 2');
                        end
                        phase = 2;
                    elseif inw && fix_init < t_a
                        % Now waiting for the subject to fixate long enough
                        % to start trial.
                        fix_init = GetSecs() - t0;
                    elseif ~inw && fix_init < t_a
                        % The subject failed to fixate while the targe was
                        % displayed. It aborts the trial and goes to the
                        % next.
                        if self.EyeLink
                            Eyelink('MESSAGE','ABORT TRIAL');
                        end
                        ok = 0;
                    end
                    continue
                end
                
                
                % =====================================================
                % Fixation offset
                % =====================================================
                
                if ~fixation_offset
                    % Target
                    cx = self.scn.w/2;
                    smp = sp_(cx,d2p(self.target.w),d2p(self.target.h));
                    Screen(self.target.shape,self.scn.p,tc(),smp);
                    
                    % Distractor
                    cx = self.scn.w/2 - d2p(self.sacc_s ...
                        - self.distractors(trial));
                    smp = sp_(cx,d2p(self.fix.w),d2p(self.fix.h));
                    Screen(self.fix.shape,self.scn.p,self.dist_c,round(smp));

                    
                    Screen('Flip',self.scn.p);
                    fixation_offset = 1;
                    % How long does the subject has to land in the goal.
                    t_a = t_a + self.sacc_t;
                    % Timing of the saccade
                    saccade_time = GetSecs();
                    continue
                end
                
                
                % =====================================================
                % Target moves
                % =====================================================
                
                % Something is not quite working
                
                if phase == 2
                    if GetSecs() - saccade_time > self.disp_t
                        % Move the target by the desired shift
                        shift = self.trials(trial);
                        cx = d2p(shift) + self.scn.w/2;
                        smp = sp_(cx,d2p(self.target.w),d2p(self.target.h));
                        Screen(self.target.shape,self.scn.p,tc(),smp);
                        
                        % Keep distractor
                                    
                        cx = self.scn.w/2 - d2p(self.sacc_s - self.distractors(trial));
                        smp = sp_(cx,d2p(self.fix.w),d2p(self.fix.h));
                        Screen(self.fix.shape,self.scn.p,self.dist_c,round(smp));
                        
                        
                        Screen('Flip',self.scn.p);
                        if self.EyeLink
                            % Phase three started
                            Eyelink('MESSAGE','PHASE 3');
                        end
                        
                        
                        % Escape this pase
                        phase = 3;
                        % Restart the fixation accumulator.
                        fix_init = 0;
                        % New window with more liberal parameters
                        w = [-d2p(self.l_w),d2p(self.l_w), ... 
                                    d2p(self.l_w),-d2p(self.l_w)];
                        % New fixation target.
                        fx = [self.scn.w/2 + d2p(self.sacc_s/2),self.scn.h/2];
                    elseif GetSecs() - t0 > t_a
                        % The subject failed to fixate to the new target
                        % position at time.
                        if self.EyeLink
                            Eyelink('MESSAGE','ABORT TRIAL');
                        end
                        ok = 0;
                    end
                    continue
                end
                
                if phase == 3
                    % Current time, save a bit by not calling the cpu clock
                    % to much
                    ct = GetSecs();
                    % In window? Only one call
                    inw = inwindow(fx,mx,my,w);
                    if self.EyeLink
                        evtype = Eyelink('getnextdatatype');
                    end
                    if ct - t0 > t_a
                        % The subject failed to fixate to the new target
                        % position at time.
                        if self.EyeLink
                            Eyelink('MESSAGE','ABORT TRIAL');
                        end
                        ok = 0;
                    elseif self.EyeLink && el.ENDSACC == evtype && ~inw
                        % The subject's saccaded landed far away from the
                        % target. Possibiy in the middle of the screen
                        if self.EyeLink
                            Eyelink('MESSAGE','ABORT TRIAL');
                        end
                        ok = 0;
                    elseif fix_init == 0 && ~inw
                        % Subject hasnt reached yet the goal
                    elseif fix_init == 0 && inw
                        % Subject reached the goal, start counting time
                        fix_init = ct;
                    elseif ct - fix_init < self.goal_t && inw
                        % Subject has reached the target, now waiting for
                        % the subject to fixate long enough
                        continue
                    elseif ct - fix_init > self.goal_t && inw
                        % Subject reached target and fixated long enough,
                        % keep going.
                        if self.EyeLink
                            % PHASE 4 started
                            Eyelink('MESSAGE','PHASE 4');
                        end
                        phase = 4;
                    elseif ct - fix_init > self.goal_t && ~ inw
                        % Subject reached the target but then look far away
                        % Cancel trial
                        if self.EyeLink
                            Eyelink('MESSAGE','ABORT TRIAL');
                        end
                        ok = 0;
                    end
                    continue
                end
                
                
                % Clean screen
                Screen('Flip',self.scn.p);
                
                if phase == 4
                    % Every thing went well.
                    if self.EyeLink
                        Eyelink('MESSAGE','TRIAL ACEPTED');
                    end
                    break
                end
                
            end
            % Collect behavioral response and send it to the EDF file.
            KbEventFlush();
            
            if ok
                % Clean up screen and wait for behavioral response
                Screen(self.scn.p, 'Flip');
                t = GetSecs();
                while self.beh_t > GetSecs() - t;
                    [avail,~,k] = KbCheck();
                    % Take only the firs response
                    if avail
                        self.bh_r(trial) = find(k);
                        fprintf(1,'Subject pressed %d\n.',find(k));
                        break
                    end
                end
                while self.beh_t > GetSecs() - t
                end
                
                % Send message to the EyeLink
                if self.EyeLink
                    if avail
                        Eyelink('MESSAGE',sprintf('KEY %d',find(k)));
                    else
                        Eyelink('MESSAGE','KEY nan');
                    end
                end
                % Buffer to send the key
                WaitSecs(0.20);
            else
                fprintf('Block %d, trial %d, aborted.\n',...
                    floor(trial/self.num_trials)+1, ...
                    trial - floor(trial/self.num_trials)*self.num_trials);
            end
            % Stop recording with the eyetracker
            if self.EyeLink
                Eyelink('Closefile');
                Eyelink('ReceiveFile');
            end
            
            if ~ok
                self.centeredText(errortype,tc());
                WaitSecs(1);
            end
            
            % Intertrial
            % =============================================================
            % Display a intertrial stimuli
            KbEventFlush();
            t = GetSecs();
            
            % Display a cross in the screen during the intertrail interval.
            self.centeredText('+',tc(),20);
            
            while 1 > GetSecs() - t
                % Pause Trial if key is pressed
                [avail,~,k] = KbCheck();
                if avail
                    KbEventFlush();
                    self.centeredText(['Press ''q'' to quit or ''c'' to calibrate.' ...
                        'Press any other key to continue. '])
                    % Buffer.
                    WaitSecs(0.5);
                    while 1
                        [avail,~,k] = KbCheck();
                        if avail;
                            break
                        end
                    end
                    % If the key is q
                    if find(k) == KbName('q')
                        goout = 1;
                        break
                        % If it's v, calbrate
                    elseif find(k) == KbName('c')
                        calibrateEL(self.scn.p,self.scn.mon,0,...
                            self.bc(b).bg,self.bc(b).tc);
                    end
                    KbEventFlush();
                    break
                end
            end
            
            function fix = inwindow(cx,mx,my,fix_win)
                % determine if gx and gy are within fixation window
                if nargin < 4
                    fix_win = self.fix_win;
                end
                fix = all([mx > (cx(1) + fix_win(1))
                    my < (cx(2) + fix_win(2))
                    mx < (cx(1) + fix_win(3))
                    my > (cx(2) + fix_win(4))
                    ]);
            end
            
            
            function c = tc()
                % target color
                c = self.bc(b).tc;
            end
            
            
        end
        
        function sendParameters(self,trial)
            % Send information to the eyetracker which is worth to store.
            [t,b] = ind2sub([self.num_trials self.num_blocks],trial);
            Eyelink('Message',['BLOCK ' num2str(self.blocks(b))]);
            Eyelink('Message',['TRIAL ' num2str(t)]);
            Eyelink('Message',['SUBJECT ' num2str(self.exp_par.sbj)]);
            Eyelink('Message',sprintf('AMPLITUDE %0.4f',self.trials(t,b)))
            Eyelink('Message',sprintf('DISTRACTOR %0.4f',self.distractors(t,b)))
        end
        
    end
end